<?php

// Namespace

namespace Loop;


/**
 * CPathHelper is a helper that provides useful methods to manipulate and transform strings
 *
 * @package		Loop
 * @category	Helper
 */

class CPathHelper
{
	/**
	 * Converts an extension ID into the namespace of the extension
	 * 
	 * @param 	string 	$extensionId 	The extension id
	 * 
	 * @return 	string 					The namespace
	 */
	
	public function convertExtensionIdToNamespace($extensionId)
	{
		$namespace = '\\' . str_replace( '-' , '\\', $extensionId) . '\\';


		return $namespace;
	}
	
	
	/**
	 * Converts an extension ID into the path to the extension
	 *
	 * @param	int		$extensionId	The ID of the extension
	 *
	 * @return	string					The path to the extension
	 */
	
	public function convertExtensionIdToPath($extensionId)
	{
		// The default path is to the application

		$pathToExtension = PATH_APPLICATION;


		// Is there an extension?

		if (empty($extensionId) == false)
		{
			// Does the PATH constant exist?

			$constantName = 'PATH_' . strtoupper(str_replace('-', '_', $extensionId));

			if (defined($constantName) == false)
			{
				e
				(
					EXCEPTION_CONSTANT_NOT_FOUND,
					array
					(
						'constantName' => $constantName,
						'extensionId' => $extensionId
					)
				);
			}


			// Get it!

			$pathToExtension = constant($constantName);
		}


		return $pathToExtension;
	}
	
	
	/**
	 * Converts a path to fragments
	 *
	 * @param	string	$path	The path (e.g. '/Html/Page.Home.html')
	 *
	 * @return	array			The corresponding fragments
	 */
	
	public function convertPathToFragments($path)
	{
		// Extract information about the view
		
		$pattern = '/^';
		$pattern .= '(\/?\[([a-zA-Z\-]+)?\])?';	// The extension ID
		$pattern .= '\/';
		$pattern .= '([a-zA-Z]+)';				// The view type
		$pattern .= '\/';
		$pattern .= '([a-zA-Z0-9\.\-_]+)';		// The view class + name
		$pattern .= '\.';
		$pattern .= '([a-zA-Z]+)';				// The file extension
		$pattern .= '$/';
		
		if (preg_match($pattern, $path, $matches) == false)
		{
			e
			(
				EXCEPTION_VIEW_PATH_NOT_VALID,
				array('path' => $path)
			);
		}
		
		
		// Grab the view class and view name
		
		$viewClassAndName = explode('.', $matches[4]);
		$viewClass = $viewClassAndName[0];
		$viewName = implode('.', array_slice($viewClassAndName, 1));
		
		
		// A view name is always necessary, whereas the view class is optional
		// Hence switch the two if necessary
		
		if
		(
			(empty($viewClass) == false) &&
			(empty($viewName) == true)
		)
		{
			$tmp = $viewClass;
			$viewClass = $viewName;
			$viewName = $tmp;
		}
		
		
		// Fill-in the fragments
		
		$fragments = array();
		
		$fragments['extensionId'] = $matches[2];
		$fragments['viewType'] = $matches[3];
		$fragments['viewClass'] = $viewClass;
		$fragments['viewName'] = $viewName;
		$fragments['viewFileExtension'] = $matches[5];
		
		
		return $fragments;
	}
	
	
	/**
	 * Converts a URI to fragments
	 *
	 * @param	string	$uri	The URI (e.g. '/page/home/')
	 *
	 * @return	array			The corresponding fragments
	 */
	
	public function convertUriToFragments($uri)
	{
		// Extract fragments from the URL

		$uri = str_replace('%5B', '[', str_replace('%5D', ']', $uri));
		
		$pattern = '/^';
		$pattern .= '(\/\[([a-zA-Z\-]+)\])?';					// Name of the extension (in 2)
		$pattern .= '(\/([a-z]{2,3}))?';						// Language ISO code (in 4)
		$pattern .= '(\/(action|api|fast|page|shared|view))';	// Group of the view (in 6)
		$pattern .= '(\/(.*))?';								// Rest of the URL (in 8)
		$pattern .= '/';
		
		
		if (preg_match($pattern, $uri, $matches) == false)
		{
			// The URI seems to be incorrect, trigger error
			e
			(
				EXCEPTION_VIEW_URI_NOT_VALID,
				array('uri' => $uri)
			);
		}
		
		// Grab the view group and remaining URI
		
		$viewGroup = $matches[6];
		$lastUriFragment = $matches[8];
		
		
		// Fill-in the fragments
		
		$fragments = array();
		
		$fragments['extensionId'] = $matches[2];
		$fragments['languageIsoCode'] = $matches[4];


		if ($viewGroup == 'action')
		{
			// Extract the rest of the URI
			
			$pattern = '/^';
			$pattern .= '([a-zA-Z\.]+)';
			$pattern .= '$/';
			
			if (preg_match($pattern, $lastUriFragment, $matches) == false)
			{
				e
				(
					EXCEPTION_VIEW_URI_NOT_VALID,
					array('uri' => $uri)
				);
			}
			
			
			// Fill-in fragments
			
			$fragments['viewType'] = VIEW_TYPE_ACTION;
			$fragments['viewClass'] = null;
			$fragments['viewName'] = $matches[1];
			$fragments['viewFileExtension'] = null;
		}
		else if ($viewGroup == 'api')
		{
			// Extract the rest of the URI
			
			$pattern = '/^';
			$pattern .= '([a-z]+)';
			$pattern .= '\/';
			$pattern .= '([a-zA-Z_]+\.[a-zA-Z]+)';
			$pattern .= '$/';
			
			if (preg_match($pattern, $lastUriFragment, $matches) == false)
			{
				e
				(
					EXCEPTION_VIEW_URI_NOT_VALID,
					array('uri' => $uri)
				);
			}
			
			
			// Fill-in fragments
			
			$fragments['viewType'] = VIEW_TYPE_API;
			$fragments['viewClass'] = null;
			$fragments['viewName'] = $matches[2];
			$fragments['viewFileExtension'] = $matches[1];
		}
		else if ($viewGroup == 'fast')
		{
			$path = '/[' . $fragments['extensionId'] . ']/' . $viewGroup . '/' . $lastUriFragment;
			$fragments = array_merge($fragments, $this->convertPathToFragments($path));
		}
		else if ($viewGroup == 'page')
		{
			// Extract the rest of the URI
			
			$viewNameFragments = explode('/', $lastUriFragment);
			$lastNameFragment = array_pop($viewNameFragments);
			
			if (empty($lastNameFragment) == false)
			{
				array_push($viewNameFragments, $lastNameFragment);
			}
			
			
			// Create 2 alternative view names (Page.Object.Object and Page.Object.method)
			
			foreach ($viewNameFragments as &$viewNameFragment)
			{
				$viewNameFragment = ucfirst($viewNameFragment);
			}
			
			$viewName = implode('.', $viewNameFragments);
			
			
			// Fill-in fragments
			
			$fragments['viewType'] = VIEW_TYPE_HTML;
			$fragments['viewClass'] = 'Page';
			$fragments['viewName'] = $viewName;
			$fragments['viewFileExtension'] = 'html';
		}
		else if ($viewGroup == 'shared')
		{
			// Dependencies

			require_once(PATH_LOOP . 'Model/CSharedObject.php');
			

			// Load the shared object from the token on the URL

			$sharedObject = new \Loop\CSharedObject();
			$sharedObject->loadFromToken(fv('t', true));
			
			
			// Now get its target URI

			$targetUri = $sharedObject->getTargetUri();


			// And extract fragments out of it

			$fragments = $this->convertUriToFragments($targetUri);


			// The view must bypass security checks
			// @todo Might be dangerous, authorize only the view + shared object?

			pref('[Loop]/features/authentication', false);
			pref('[Loop]/features/permissions', false);


			// Prepare variables for the view

			$fragments['viewVariables'] = array
			(
				'className' => $sharedObject->getObjectClassName(),
				'extensionId' => $sharedObject->getObjectExtensionId(),
				'objectId' => $sharedObject->getObjectId()
			);
		}
		else if ($viewGroup == 'view')
		{
			$path = '/[' . $fragments['extensionId'] . ']/' . $lastUriFragment;
			$fragments = array_merge($fragments, $this->convertPathToFragments($path));
		}
		else
		{
			e
			(
				EXCEPTION_VIEW_URI_NOT_VALID,
				array('uri' => $uri)
			);
		}
		
		
		return $fragments;
	}
	

	/**
	 * Converts a path into an extensionId
	 *
	 * @param	int		$path	The path to extension
	 *
	 * @return	string			The extensionId
	 */
	
	public function convertPathToExtensionId($path)
	{
		// Get the extension ids

		$extensionsIds = app()->getExtensionManager()->getExtensionsIds();


		// For each extension found

		foreach ($extensionsIds as $extensionId)
		{
			// Does the PATH constant exist?

			$constantName = 'PATH_' . strtoupper(str_replace('-', '_', $extensionId));

			$pathToExtension = constant($constantName);


			// Does the path correspond ?

			if ($pathToExtension == $path)
			{
				return $extensionId;
			}
		}
	}
	

	/**
	 * Creates an absolute URL from a URI
	 *
	 * @param	string	$uri					The URI (e.g. 'page/home/')
	 * @param	array	$arguments				A list of arguments (key + value)
	 * @param 	boolean $useTimeOfDeployment 	True to add the time of deployment into the uri fragments, false otherwise
	 *
	 * @return	string				The URL
	 */
	
	public function convertUriToUrl($uri = null, $arguments = null , $useTimeOfDeployment = true)
	{
		// Extract fragments from the URL
		
		$pattern = '/^';
		$pattern .= '(\[([a-zA-Z\-]+)\]\/)?';								// Name of the extension (in 2)
		$pattern .= '((action|api|data|fast|page|shared|static|view)\/)?';	// Group of the view (in 4)
		$pattern .= '(.*)';													// Rest of the URL (in 5)
		$pattern .= '/';
		
		if (preg_match($pattern, $uri, $matches) == false)
		{
			// The URI seems to be incorrect, trigger error
			
			e
			(
				EXCEPTION_VIEW_URI_NOT_VALID,
				array('uri' => $uri)
			);
		}
		
		
		// Grab the view group and remaining URI
		
		$extensionId = $matches[2];
		$viewGroup = $matches[4];
		$lastUriFragment = $matches[5];
		
		
		// Get the root of the URL (localized or not?)
		
		if
		(
			(
				($viewGroup === 'data') ||
				($viewGroup === 'static')
			)
			&&
			(pref('[Loop]/features/cdn') === true)
		)
		{
			$url = $this->getRootUrl(false, pref('[Loop]/cdn/prefix'));
		}
		else
		{
			$url = $this->getRootUrl(false);
		}
		
		
		// Add the extension
		
		if (empty($extensionId) == false)
		{
			$url .= '[' . $extensionId . ']/';
		}
		
		
		// Add the language ID if it's a view
		
		if
		(
			($viewGroup == 'view') ||
			($viewGroup == 'page')
		)
		{
			$url .= pref('[Loop]/localization/languageIsoCode') . '/';
		}
		
		
		// Should we go to the home page?
		
		if (empty($uri) == true)
		{
			$url = app()->getPathHelper()->convertPathToUrl(pref('[Loop]/views/pathToPageHome.html'));
		}
		else
		{
			$url .= $viewGroup;

			if (empty($viewGroup) == false)
			{
				$url .= '/';
			}

			$url .= $lastUriFragment;
		}
		
		
		// Make sure we've got an array of arguments
		
		if (is_array($arguments) == false)
		{
			$arguments = array();
		}
		
		
		// Check whether URL already has arguments

		$urlFragments = explode('?', $url);

		if (count($urlFragments) > 1)
		{
			// Ensure arguments are removed from the URL
			
			$url = $urlFragments[0];


			// Then handle arguments one by one

			$rawArguments = explode('&', $urlFragments[1]);

			foreach ($rawArguments as $rawArgument)
			{
				// Extract fragments of the argument

				$argumentFragments = explode('=', $rawArgument);


				// Save the argument

				if (isset($argumentFragments[0]) == true)
				{
					if (isset($argumentFragments[1]) == true)
					{
						$arguments[$argumentFragments[0]] = $argumentFragments[1];
					}
					else
					{
						$arguments[$argumentFragments[0]] = null;
					}
				}
			}
		}
			
		
		// Add the time of deployment to arguments

		$timeOfDeployment = app()->getPreferenceManager()->getTimeOfDeployment();
		
		if
		(
			($viewGroup != 'action') &&
			($viewGroup != 'api') &&
			($viewGroup != 'page') &&
			(empty($timeOfDeployment) == false) &&
			($useTimeOfDeployment == true)
		)
		{
			$arguments['d'] = $timeOfDeployment;
		}


		//

		$key = fv('key', false);

		if (empty($key) === false)
		{
			$arguments['key'] = $key;
		}


		// Append arguments to the URL
		
		if (empty($arguments) == false)
		{
			if (strpos($url, '?') === false)
			{
				$url .= '?';
			}
			else
			{
				$url .= '&';
			}
			
			$separator = null;
			
			foreach ($arguments as $argumentName => $argumentValue)
			{
				$url .= $separator . $argumentName . '=' . $argumentValue;
				$separator = '&';
			}
		}
		
		
		return $url;
	}


	/**
	 * Concatenate the time of deployment into the url
	 *
	 * @param 	string 	$url 		The URL
	 *
	 * @return 	string 				The corresponding URL 
	 */

	public function convertUrlToUrl($url)
	{
		$timeOfDeployment = app()->getPreferenceManager()->getTimeOfDeployment();

		if (empty($timeOfDeployment) === false)
		{
			if (strpos($url, '?') == false)
			{
				$url .= '?';
			}
			else
			{
				$url .= '&';
			}

			$url .= 'd=' . $timeOfDeployment;
		}


		return $url;
	}
	
	
	/**
	 * Converts a path to a URI
	 *
	 * @param	string	$path	The path (e.g. '/Html/Page.Home.html')
	 *
	 * @return	string			The corresponding URI
	 */
	
	public function convertPathToUri($path = null)
	{
		$fragments = $this->convertPathToFragments($path);
		$uri = null;
		
		if (empty($fragments['extensionId']) == false)
		{
			$uri = '[' . $fragments['extensionId'] . ']/';
		}
		
		if
		(
			($fragments['viewType'] == 'Html') &&
			($fragments['viewClass'] == 'Page')
		)
		{
			$uri .= 'page/';
			$uri .= strtolower(str_replace('.' , '/', $fragments['viewName'])) . '/';
		}
		else if ($fragments['viewType'] == 'Action')
		{
			$uri .= 'action/' . $fragments['viewName'] . '.' . $fragments['viewFileExtension'];
		}
		else
		{
			$uri .= 'view/';
			$uri .= $fragments['viewType'] . '/';

			if (empty($fragments['viewClass']) == false)
			{
				$uri .= $fragments['viewClass'] . '.';
			}
			
			if (empty($fragments['viewName']) == false)
			{
				$uri .= $fragments['viewName'] . '.';
			}
			
			$uri .= $fragments['viewFileExtension'];
		}
		
		
		return $uri;
	}
	
	
	/**
	 * Converts a path to a URL
	 *
	 * @param	string	$path				The path (e.g. '/Html/Page.Home.html')
	 * @param	array	$arguments			A list of arguments (key + value)
	 * @param	boolean	$timeParameter		Activate the time parameter in the URL (d=time)
	 *
	 * @return	string						The corresponding URL
	 */
	
	public function convertPathToUrl($path = null, $arguments = null, $timeParameter = true)
	{
		$uri = $this->convertPathToUri($path);
		$url = $this->convertUriToUrl($uri, $arguments, $timeParameter);
		
		
		return $url;
	}


	/**
	 * Gets the current url
	 *
	 * @return 	string 		The current url
	 */

	public function getCurrentUrl()
	{
		// Define the scheme
		
		if (is_null(v('SERVER::HTTPS')) == true)
		{
			$url = 'http://';
		}
		else
		{
			$url = 'https://';
		}
		
		
		// Add the host

		$url .= v('SERVER::SERVER_NAME');

		
		// Add the port

		if (v('SERVER::SERVER_PORT') != '80')
		{
			$url .= ':' . v('SERVER::SERVER_PORT');
		}


		// Add the request URI

		if (is_null(v('SERVER::REQUEST_URI')) == false)
		{
			$url .= v('SERVER::REQUEST_URI');
		}


		// Add the request URI

		if (is_null(v('SERVER::QUERY_STRING')) == false)
		{
			$url .= v('SERVER::QUERY_STRING');
		}


		return $url;
 	}


	/**
	 * Gets the path to a class
	 *
	 * @param	string	$className		The class name / model name
	 * @param 	string 	$extensionId 	The extension id
	 * @return	string					The full path to the model if found, null otherwise
	 */
	
	public function getPathToClass($className, $extensionId = null)
	{
		return $this->getPathToObject($className, $extensionId, 'Class');
	}


	/**
	 * Gets the path to a model
	 *
	 * @param	string	$className		The class name / model name
	 * @param 	string 	$extensionId 	The extension id
	 *
	 * @return	string					The full path to the model if found, null otherwise
	 */
	
	public function getPathToModel($className, $extensionId = null) 
	{
		return $this->getPathToObject($className, $extensionId, 'Model');
	}


 	/**
 	 * Gets the path to an object
 	 *
 	 * @param	string	$className		The class name / model name
 	 * @param 	string 	$extensionId 	The extension id
 	 * @param 	string 	$folder 		The folder name
	 *
	 * @return 	string					The full path to the object if found, null otherwise
 	 */

 	private function getPathToObject($className, $extensionId, $folder)
 	{
		// Is there an extensionId

		if (is_null($extensionId) === false)
		{
			$pathsToExtensions[] = $this->convertExtensionIdToPath($extensionId) ;
		}
		else
		{
			// Look for model in each extension
			
			$pathsToExtensions = app()->getExtensionManager()->getPathsToExtensions();
		}
		
		foreach ($pathsToExtensions as $pathToExtension)
		{
			// Does the object exist?
			
			$pathToObject = $pathToExtension . $folder . '/' . $className . '.php';
			
			if (file_exists($pathToObject) == true)
			{
				// It does, return path
				
				return $pathToObject;
			}
		}
 	}


	/**
	 * Gets the current or global namespace
	 *
	 * @param	bool	$isGlobal	True to get the global namespace, false to get the current one
	 *
	 * @return	string				The namespace
	 */

	public function getNamespace($isGlobal = false)
	{
		// Get the view
		
		$view = null;
		
		if ($isGlobal == true)
		{
			$view = view();
		}
		else
		{
			$view = app()->getViewManager()->getCurrentView(); 
		}
		
		
		// Get the namespace
		
		$namespace = null;
		
		if (is_object($view) == true)
		{
			$namespace = $view->getNamespace();
		}
	}
	
	
	/**
	 * Returns the root URL on which the website is currently executed (e.g. https://www.mywebsite.com/test/)
	 *
	 * @param	bool	$includeLanguageIsoCode	True to append the language ISO code, false otherwise
	 * @param	string	$subDomain				An optional subdomain to be appended before the host name
	 *
	 * @return	string							The URL
	 */
	
	public function getRootUrl($includeLanguageIsoCode = true, $subDomain = null)
	{
		// Define the host (either the server name, or the host of environment)
		
		$host = v('SERVER::SERVER_NAME') . '/';

		$environments = pref('[Loop]/deployment/environments');
		$hosts = $environments[env()];

		if (is_array($hosts) == false)
		{
			$host = $hosts;
		}
		
		
		// Define the scheme
		
		if (is_null(v('SERVER::HTTPS')) == true)
		{
			$scheme = 'http';
		}
		else
		{
			$scheme = 'https';
		}
		
		
		// Build the final URL
		
		$url = $scheme . '://' . $subDomain . $host;
		
		
		// Should we add the language?
		
		if ($includeLanguageIsoCode == true)
		{
			$url .= pref('[Loop]/localization/languageIsoCode') . '/';
		}
		
		
		return $url;
	}
}

?>
